﻿using SketchUpAPI.NET.Objects;
using SketchUpAPI.NET.SketchUp;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace SketchUpAPI.NET.Geometry.Intersect
{
    /// <summary>
    /// 求相交
    /// </summary>
    public class Intersection
    {
        private static double Tolerance = SketchUpApplication.GlobalTolerance;

        /// <summary>
        /// 比较两个double类型大小
        /// </summary>
        /// <param name="x"></param>
        /// <param name="y"></param>
        /// <returns></returns>
        private static bool LessThan(double x, double y)
        {
            return x - y < -Tolerance;
        }

        /// <summary>
        /// 比较连个数是否相等
        /// </summary>
        /// <param name="x"></param>
        /// <param name="y"></param>
        /// <returns></returns>
        private static bool Equal(double x, double y)
        {
            return !LessThan(x, y) && !LessThan(y, x);
        }

        /// <summary>
        /// 直线与平面的交点
        /// </summary>
        /// <param name="line"></param>
        /// <param name="plane"></param>
        /// <param name="parameter">返回直线上的参数</param>
        /// <returns>是否有交点</returns>
        public static bool LinePlane(Line line, Plane3D plane, out double parameter)
        {
            /* 使用直线的参数方程与平面方程求交点
             * 参数方程：
             *  x = x1 + t*m
             *  y = y1 + t*n
             *  z = z1 + t*p
             *  其中 (x1, y1, z1) 为直线上的点；(m,n,p) 为直线的方向向量
             *  
             *  直线与平面平行的判别式为：mA + nB + pC = 0
             */

            Point3D start = line.Start;
            Vector3D dir = line.Direction;

            double m = dir.X;
            double n = dir.Y;
            double p = dir.Z;

            double A = plane.a;
            double B = plane.b;
            double C = plane.c;
            double D = plane.d;

            double para = m * A + n * B + p * C;
            if (Equal(para, 0))
            {
                parameter = 0;
                return false;
            }

            parameter = -(A * start.X + B * start.Y + C * start.Z + D) / para;
            parameter /= line.Length;

            return true;
        }

        /// <summary>
        /// 两个平面的交线
        /// </summary>
        /// <param name="plane1"></param>
        /// <param name="plane2"></param>
        /// <param name="intersectionLine"></param>
        /// <returns>是否存在交线</returns>
        public static bool PlanePlane(Plane3D plane1, Plane3D plane2, out Line intersectionLine)
        {
            /* 使用平面的参数方程求解
             */

            if (plane1.Normal.IsParallelTo(plane2.Normal))
            {
                intersectionLine = null;
                return false;
            }

            Vector3D dir = plane1.Normal.CrossProduct(plane2.Normal).Unit;

            // 设 x == 0 则解方程可求的 y、z
            double y = 0;
            double z = 0;
            if (Equal(plane1.b, 0))
            {
                z = (plane2.b * plane1.d - plane1.b * plane2.d) / (plane1.b * plane2.c - plane2.d * plane1.c);
                y = -(plane1.c * z + plane1.d) / plane1.b;
            }
            else
            {
                z = plane1.d / plane1.c;
                y = (plane2.c * z + plane2.d) / plane2.b;
            }

            Point3D start = new Point3D(0, y, z);
            Point3D end = start + dir * 10;
            intersectionLine = new Line(start, end);

            return true;
        }

        /// <summary>
        /// 两个直线的交点
        /// </summary>
        /// <param name="line1"></param>
        /// <param name="line2"></param>
        /// <param name="t1">交点在直线1上的参数</param>
        /// <param name="t2">交点在直线2上的参数</param>
        /// <returns></returns>
        public static bool LineLine(Line line1, Line line2, out double t1, out double t2)
        {
            /* 使用直线的参数方程求交点
             * 参数方程：
             *  x = x1 + t*m
             *  y = y1 + t*n
             *  z = z1 + t*p
             *  其中 (x1, y1, z1) 为直线上的点；(m,n,p) 为直线的方向向量
             *  
             *  两根直线上各取一点连成第三条直线，即可判断三直线是否共面：三个方向向量的混合积为0
             */

            t1 = 0;
            t2 = 0;

            Vector3D m = line1.Start - line2.Start;
            double cro = m.DotProduct(line1.Direction.CrossProduct(line2.Direction));

            if (!Equal(cro, 0)) return false;

            Vector3D dir1 = line1.Direction;
            Vector3D dir2 = line2.Direction;

            t1 = (dir2.X * (line1.Start.Y - line2.Start.Y) - dir2.Y * (line1.Start.X - line2.Start.X)) / (dir1.X * dir2.Y - dir2.X * dir1.Y);
            Point3D interPt = line1.PointAtLength(t1);

            t1 /= line1.Length;

            if (!interPt.Equals(line2.Start))
            {
                t2 = (interPt.X - line2.Start.X) / dir2.X / line2.Length;
            }

            return true;
        }
    }
}